/**
 * release script
 */
// env
require('../env');

const path = require('path');
// const axios = require('axios');
const fsp = require('fs-extra');
const Promise = require('bluebird');
const spawn = require('child-process-promise').spawn;

const pkgs = require('../package.json');
const getPaths = require('../webpack.paths');
const qiniu = require('../app/services/qiniu');
const loggers = require('../app/services/logger').loggers;
const exec = require('child-process-promise').exec;

const info = loggers.INFO;
const warn = loggers.WARN;

const distBasePath = path.join(__dirname, '../dist-views');
const manifestsMapPath = path.join(distBasePath, 'manifests-map.json');

const PROJECT_NAME = pkgs.projectName || pkgs.name;
const IO_MAX_CONCURRENCY = require('os').cpus().length;
const CDN_BUCKET = process.env.CDN_BUCKET;
const manifestsMap = {};

// Main
Promise.try(() => {
    info('Read last manifests map...');

    return fsp.readJSON(manifestsMapPath)
    .catch(err => {
        warn('Read last manifests map error:', err.message);

        return {};
    })
    .then(map => {
        Object.assign(manifestsMap, map);
    });
})
.then(() => {
    info('Start building...');

    return spawn('npm', ['run', 'build'], {
        stdio: 'inherit'
    });
})
.then(() => {
    info('Read new manifests...');

    return Promise.try(() => {
        return fsp.readdir(distBasePath);
    })
    .map(name => {
        const paths = getPaths({ appName: name });
        const module = {
            name: name,
            modulePath: paths.DIST,
            manifestPath: paths.MANIFEST,
            manifest: null,
            removeFiles: [],
            syncFiles: [],
            paths: paths
        };

        return fsp.readJSON(paths.MANIFEST)
        .catch(() => {
            return null;
        })
        .then(manifest => {
            module.manifest = manifest;

            return module;
        });
    })
    .filter(module => {
        return !!module.manifest;
    });
})
.map(module => {
    const oldManifest = manifestsMap[module.name];
    const removeFiles = module.removeFiles;
    const syncFiles = module.syncFiles;
    const manifest = module.manifest;

    if(oldManifest) {
        info(`[${module.name}] Check out-of-date files...`);

        Object.keys(oldManifest).forEach(name => {
            const oldDist = oldManifest[name];
            const dist = manifest[name];

            if(!dist || dist !== oldDist) {
                removeFiles.push({
                    dist: oldDist,
                    src: name
                });
            }
        });
    }

    info(`[${module.name}] Check new files...`);

    Object.keys(manifest).forEach(name => {
        const oldDist = oldManifest && oldManifest[name];
        const dist = manifest[name];

        if(!oldDist || dist !== oldDist) {
            syncFiles.push({
                dist: dist,
                src: name
            });
        }
    });

    return module;
})
.mapSeries(module => {
    info(`[${module.name}] Clear out-of-date files...`);

    return Promise.map(module.removeFiles, ({dist}) => {
        info(`[${module.name}] Remove file:`, dist);

        return fsp.remove(dist);
    }, {
        concurrency: IO_MAX_CONCURRENCY
    })
    .then(() => {
        return module;
    });
})
// .mapSeries(module => {
//     info(`[${module.name}] Sync new files...`);

//     return Promise.map(module.syncFiles, ({dist}) => {
//         const paths = module.paths;
//         const distPath = path.join(paths.DIST, dist);
//         const cdnDistPath = path.join(paths.ASSETS_PATH, dist);

//         info(`[${module.name}] Sync file:`, dist, '->', cdnDistPath);

//         return qiniu.upload(distPath, cdnDistPath, CDN_BUCKET);
//     }, {
//         concurrency: IO_MAX_CONCURRENCY
//     })
//     .then(() => {
//         return module;
//     });
// })
.mapSeries(module => {
    return Promise.try(() => {
        return exec('git log -1 --format="%H"');
    })
    .then(({ stdout }) => {
        return String(stdout).slice(0, 7);
    })
    .then(hash => {
        if(!module.syncFiles.length) {
            info(`[${module.name}] Manifest not change.`);

            return;
        }

        info(`[${module.name}] Update server manifest...`);

        const PROJECT_NAME = module.paths.PROJECT_NAME;
        const cdnDistPath = path.join(PROJECT_NAME, `${module.name}/manifest-${hash}.json`);

        return Promise.try(() => {
            info(`[${module.name}] Uploading manifest map -> ${cdnDistPath}`);

            const data = module.manifest;
            const buf = Buffer.from(JSON.stringify(data));

            return qiniu.upload(buf, cdnDistPath, CDN_BUCKET);
        });
    })
    .then(res => {
        if(res && res.data) {
            info(`[${module.name}] Server manifest Updated:`, res.data || res);

            // Assign manifestId
            module.manifestId = res.data.id;
        }

        return module;
    });
})
.tap(modules => {
    info('Update manifests map...');

    const updatedModules = modules.filter(module => {
        return !!module.syncFiles.length;
    });

    updatedModules.forEach(module => {
        manifestsMap[module.name] = module.manifest;
    });

    return Promise.try(() => {
        return exec('git log -1 --format="%H"');
    })
    .then(({ stdout }) => {
        return String(stdout).slice(0, 7);
    })
    .then(hash => {
        const name = `manifests-${hash}.json`;
        const cdnDistPath = path.join(PROJECT_NAME, name);
        const buf = Buffer.from(JSON.stringify(manifestsMap));

        info('Uploading manifest map ->', cdnDistPath);

        return qiniu.upload(buf, cdnDistPath, CDN_BUCKET);
    })
    .then(res => {
        info('Server manifests map Updated:', res.data || res);

        return fsp.outputJSON(manifestsMapPath, manifestsMap);
    })
    .then(() => {
        info('Local manifests map Updated.');
    });
})
.then(() => {
    info('😄 Congratulation, all jobs done. ^_^');
})
.catch(err => {
    // axios request error
    const response = err.response;
    if(response && response.data) {
        err.message += ': ' + response.data.message;
    }

    console.error(err);

    warn('😢', err.message);

    process.exit(1);
});
